package com.example.medicinebox.ui.screens

import androidx.compose.runtime.*
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.medicinebox.MbApplication
import com.example.medicinebox.data.MbRepository
import com.example.medicinebox.model.ClockAtomic
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.io.IOException

data class MbUiState(
    val showTimePicker: Boolean = false,
    val showMedicineDialog: Boolean = false,
    val namedIndex: Int = 0,
    val appTitle: String = "连接云平台中",
    val temp: Float = 0.0f,
    val humi: Int = 0,
    val showAdjustDialog: Boolean = false,
    val tempRange: ClosedFloatingPointRange<Float> = 0f..50f,
    val humiRange: ClosedFloatingPointRange<Float> = 0f..100f,
    val alarmAttach: Boolean = false
)

class MbViewModel(private val mbRepository: MbRepository): ViewModel() {
    private val _mbUiState = MutableStateFlow(MbUiState())
    val mbUiState: StateFlow<MbUiState> = _mbUiState.asStateFlow()

    /* 9 种药品名称 */
    private val _medicineName: List<MutableState<String?>> = List(9) { mutableStateOf("") }
    val medicineName: List<MutableState<String?>>
        get() = _medicineName

    private val _clocks = mutableStateListOf<ClockAtomic>()
    val clocks: List<ClockAtomic>
        get() = _clocks

    private val _checked = List(9) { mutableStateOf(false) }
    val checked: List<MutableState<Boolean>>
        get() = _checked

    /* 药品输入框 */
    private val _medicineNameInput = mutableStateOf("")
    var medicineNameInput by _medicineNameInput

    private var existAlarmMessage: String? = null

    init {
        viewModelScope.launch {
            try {
                delay(500)
                mbRepository.subscribeDevice("mybox") { result ->
                    _mbUiState.update { it.copy(appTitle = "智能药箱") }
                    mbRepository.setDefaultHandler { message ->
                        handleSubscribeMessage(message)
                    }
                }
            } catch (_: IOException) {
                _mbUiState.update { it.copy(appTitle = "网络错误") }
            }
        }
    }

    private fun makeMessagePack(message: String): HashMap<String, String> {
        val messageMap = HashMap<String, String>()
        val tokens: List<String> = message.split(',')
        for (token in tokens) {
            val keyValue = token.split('=')
            if (keyValue.size != 2) {
                continue
            }
            messageMap[keyValue[0]] = keyValue[1]
        }
        return messageMap
    }

    private fun setTemp(temp: String?) {
        if (temp == null) {
            return
        }
        _mbUiState.update {
            it.copy(
                temp = try {
                    temp.toFloat().coerceAtLeast(-30f).coerceAtMost(120f)
                } catch (_: NumberFormatException) {
                    0f
                }
            )
        }
    }

    private fun setHumi(humi: String?) {
        if (humi == null) {
            return
        }
        _mbUiState.update {
            it.copy(
                humi = try {
                    humi.toInt().coerceAtLeast(0).coerceAtMost(100)
                } catch (_: NumberFormatException) {
                    0
                }
            )
        }
    }

    private fun setTempRange(tempDown: String?, tempUp: String?) {
        if (tempUp == null || tempDown == null) {
            return
        }
        _mbUiState.update {
            it.copy(
                tempRange = try {
                    tempDown.toFloat() .. tempUp.toFloat()
                } catch (_: NumberFormatException) {
                    it.tempRange
                }
            )
        }
    }

    private fun setHumiRange(humiDown: String?, humiUp: String?) {
        if (humiDown == null || humiUp == null) {
            return
        }
        _mbUiState.update {
            it.copy(
                humiRange = try {
                    humiDown.toFloat() .. humiUp.toFloat()
                } catch (_: NumberFormatException) {
                    it.humiRange
                }
            )
        }
    }

    private fun setAlarm(alarm: String?) {
        if (alarm == existAlarmMessage) {
            return
        }
        if (alarm == null) {
            _clocks.clear()
            existAlarmMessage = null
            return
        }

        if ("1".equals(alarm)) {
            _clocks.clear()
            existAlarmMessage = alarm
            return
        }

        val alarmString = alarm.split("-")

        _clocks.clear()

        for (oneAlarm: String in alarmString) {
            val parts = oneAlarm.split(".")
            if (parts.size != 2) continue

            val timeParts = parts[0].split(":")
            if (timeParts.size != 3) continue

            val drugBits = parts[1].toInt()

            val boxList = List(9){ false }.toMutableList()
            for (j in 0 .. 8) {
                boxList[j] = (drugBits and (1 shl j)) != 0
            }

            val oneClock = ClockAtomic(timeParts[0].toInt(), timeParts[1].toInt(), 0, boxList)
            _clocks.add(oneClock)
        }

        existAlarmMessage = alarm
    }

    private fun setAlarmIsAttach(isAttach: String?) {
        if (isAttach == null) {
            return
        }
        _mbUiState.update {
            it.copy(
                alarmAttach = isAttach.equals("1")
            )
        }
    }

    private fun handleSubscribeMessage(message: String) {
        if (message == "ErrorNetwork") {
            _mbUiState.update { it.copy(appTitle = "网络错误") }
            return
        }
        val messageMap = makeMessagePack(message)
        setTemp(messageMap["temp"])
        setHumi(messageMap["humi"])
        setTempRange(messageMap["u"], messageMap["v"])
        setHumiRange(messageMap["x"], messageMap["y"])
        setAlarm(messageMap["alarm"])
        setAlarmIsAttach(messageMap["set"])
    }

    fun showTimePicker() {
        /* 清空复选框 */
        _checked.map { it.value = false }
        _mbUiState.update { it.copy(showTimePicker = true) }
    }

    fun handleMedicineNameInput(input: String) {
        medicineNameInput = input
    }

    fun closeTimePicker() {
        _mbUiState.update { it.copy(showTimePicker = false) }
    }

    fun showMedicineDialog(index: Int) {
        medicineNameInput = ""
        _mbUiState.update { it.copy(namedIndex = index, showMedicineDialog = true) }
    }

    fun closeMedicineDialog() {
        _mbUiState.update { it.copy(showMedicineDialog = false) }
    }

    fun addClock(hour: Int, minute: Int) {
        val clockAtomic = ClockAtomic(hour, minute, 0, _checked.map { it.value })
        //_clocks.add(clockAtomic)
        _mbUiState.update { it.copy(showTimePicker = false) }
        val commands = "t=%02d:%02d,m=%s".format(hour, minute, clockAtomic.getMedicinesWithoutSplit())
        viewModelScope.launch {
            mbRepository.publishControlMessage(commands)
        }
    }

    fun addMedicineName() {
        _medicineName[_mbUiState.value.namedIndex].value = medicineNameInput
        _mbUiState.update { it.copy(showMedicineDialog = false) }
    }

    fun removeClock(clock: ClockAtomic) {
        val index = _clocks.indexOf(clock) + 1
        //_clocks.remove(clock)
        val commands = "d=%d".format(index)
        viewModelScope.launch {
            mbRepository.publishControlMessage(commands)
        }
    }

    fun handlerChooseBox(index: Int) {
        _checked[index].value = !_checked[index].value
    }

    fun setAdjustDialogShow(willShow: Boolean) {
        if (willShow) {
            _mbUiState.update { it.copy(showAdjustDialog = true) }
        } else {
            _mbUiState.update { it.copy(showAdjustDialog = false) }
        }
    }

    fun handleSliderAdjust(tempSlider: ClosedFloatingPointRange<Float>, humiSlider: ClosedFloatingPointRange<Float>) {
        _mbUiState.update { it.copy(tempRange = tempSlider, humiRange = humiSlider) }
        val commands = "u=%.1f,v=%.1f,x=%.0f,y=%.0f".format(
            tempSlider.start, tempSlider.endInclusive,
            humiSlider.start, humiSlider.endInclusive
        )
        viewModelScope.launch {
            mbRepository.publishControlMessage(commands)
        }
    }

    fun handleCloseAlarm() {
        val commands = "a=0"
        viewModelScope.launch {
            mbRepository.publishControlMessage(commands)
        }
    }

    companion object {
        val Factory: ViewModelProvider.Factory = viewModelFactory {
            initializer {
                val application = (this[APPLICATION_KEY] as MbApplication)
                val mbRepository = application.container.mbRepository
                MbViewModel(mbRepository = mbRepository)
            }
        }
    }
}

